package br.com.educa.controle;

import br.com.educa.modelo.acl.AclClass;
import br.com.educa.modelo.acl.AclEntry;
import br.com.educa.modelo.acl.AclObjectIdentity;
import br.com.educa.modelo.acl.AclSid;
import br.com.educa.modelo.security.Menu;
import br.com.educa.modelo.security.Perfil;
import br.com.educa.modelo.security.Usuario;
import br.com.educa.util.CollectionUtils;
import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.roo.addon.web.mvc.controller.json.RooWebJson;
import org.springframework.roo.addon.web.mvc.controller.scaffold.RooWebScaffold;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils;

@RequestMapping("/menus")
@Controller
@RooWebScaffold(path = "menus", formBackingObject = Menu.class)
@RooWebJson(jsonObject = Menu.class)
public class MenuController {

    @RequestMapping(value = "/seu", produces = "text/html")
    public String list(Model uiModel) {
        for (Menu menu : menuService.findAllMenus()) {
            menu.setFilhos(new CollectionUtils<Menu>().toHashSet(menuService.findAllMenusChildren(menu)));
        }
        uiModel.addAttribute("menus", menuService.findAllMenus());
        return "layout/menu";
    }

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid Menu menu, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, menu);
            return "menus/create";
        }
        uiModel.asMap().clear();
        menuService.saveMenu(menu);
        return "redirect:/menus/" + encodeUrlPathSegment(menu.getId().toString(), httpServletRequest);
    }

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @RequestMapping(params = "form", produces = "text/html")
    public String createForm(Model uiModel) {
        populateEditForm(uiModel, new Menu());
        return "menus/create";
    }

    @RequestMapping(value = "/{id}", produces = "text/html")
    public String show(@PathVariable("id") Long id, Model uiModel) {
        uiModel.addAttribute("menu", menuService.findMenu(id));
        uiModel.addAttribute("itemId", id);
        return "menus/show";
    }

    @RequestMapping(produces = "text/html")
    public String list(@RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size, Model uiModel) {
        if (page != null || size != null) {
            int sizeNo = size == null ? 10 : size.intValue();
            final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;
            uiModel.addAttribute("menus", menuService.findMenuEntries(firstResult, sizeNo));
            float nrOfPages = (float) menuService.countAllMenus() / sizeNo;
            uiModel.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));
        } else {
            uiModel.addAttribute("menus", menuService.findAllMenus());
        }
        return "menus/list";
    }

    @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@Valid Menu menu, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, menu);
            return "menus/update";
        }
        uiModel.asMap().clear();
        menuService.updateMenu(menu);
        return "redirect:/menus/" + encodeUrlPathSegment(menu.getId().toString(), httpServletRequest);
    }

    @RequestMapping(value = "/{id}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("id") Long id, Model uiModel) {
        populateEditForm(uiModel, menuService.findMenu(id));
        return "menus/update";
    }

    @RequestMapping(value = "/{id}", params = "formJq", headers = "Accept=application/json", produces = "text/html")
    public String updateFormJson(@PathVariable("id") Long id, @RequestParam(value = "class", required = true) String className, Model uiModel) {
        Menu result = Menu.findMenu(id);
        String menuJson = result.toJson();
        AclObjectIdentity aoi = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(id).getSingleResult();
        List<AclEntry> aclEntrys = AclEntry.findAclEntrysByAclObjectIdentity(aoi).getResultList();
        List<Perfil> perfis = new ArrayList<Perfil>();
        for (AclEntry aclEntry : aclEntrys) {
            AclSid sid = aclEntry.getSid();
            Perfil perfil = Perfil.findPerfilsByAuthorityEquals(sid.getSid()).getSingleResult();
            if (!perfis.contains(perfil)) perfis.add(perfil);
        }
        String perfisJson = Perfil.toJsonArray(perfis);
        String json = "{\"menu\": " + menuJson + ", \"perfis\": " + perfisJson + "}";
        uiModel.addAttribute("json", json);
        return "menu/atualizarMenu";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "text/html")
    public String delete(@PathVariable("id") Long id, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size, Model uiModel) {
        Menu menu = menuService.findMenu(id);
        menuService.deleteMenu(menu);
        uiModel.asMap().clear();
        uiModel.addAttribute("page", (page == null) ? "1" : page.toString());
        uiModel.addAttribute("size", (size == null) ? "10" : size.toString());
        return "redirect:/menus";
    }

    void populateEditForm(Model uiModel, Menu menu) {
        uiModel.addAttribute("menu", menu);
        uiModel.addAttribute("menus", menuService.findAllMenus());
    }

    String encodeUrlPathSegment(String pathSegment, HttpServletRequest httpServletRequest) {
        String enc = httpServletRequest.getCharacterEncoding();
        if (enc == null) {
            enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
        }
        try {
            pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
        } catch (UnsupportedEncodingException uee) {
        }
        return pathSegment;
    }

    @RequestMapping(value = "/{id}", headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity<java.lang.String> showJson(@PathVariable("id") Long id) {
        Menu menu = menuService.findMenu(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        if (menu == null) {
            return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity<String>(menu.toJson(), headers, HttpStatus.OK);
    }

    @RequestMapping(headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity<java.lang.String> listJson() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        List<Menu> result = menuService.findAllMenus();
        return new ResponseEntity<String>(Menu.toJsonArray(result), headers, HttpStatus.OK);
    }

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @RequestMapping(method = RequestMethod.POST, headers = "Accept=application/json")
    @Transactional(rollbackFor = Throwable.class)
    public ResponseEntity<java.lang.String> createFromJson(@RequestBody String json, Principal principal) {
        final Usuario currentUser = (Usuario) ((Authentication) principal).getPrincipal();
        JSONSerializer serializador = new JSONSerializer();
        JSONDeserializer deserializador = new JSONDeserializer();
        JSONDeserializer<ArrayList<Perfil>> deserializadorPerfil = new JSONDeserializer<ArrayList<Perfil>>();
        Object objetoTranscrito = deserializador.deserialize(json);
        HashMap<String, Object> hashMap = (HashMap<String, Object>) objetoTranscrito;
        Object perfisObjeto = hashMap.get("perfis");
        Object menuObjeto = hashMap.get("menu");
        ArrayList<Perfil> perfis = deserializadorPerfil.use(null, ArrayList.class).use("values", Perfil.class).deserialize(serializador.prettyPrint(false).deepSerialize(perfisObjeto));
        Menu menu = Menu.fromJsonToMenu(serializador.prettyPrint(false).deepSerialize(menuObjeto));
        Menu paiMenu = Menu.findMenu(menu.getPai().getId());
        menu.setPai(paiMenu);
        menuService.saveMenu(menu);
        AclSid sidOwner = AclSid.findAclSidsBySidEquals("ROLE_ADMIN").getSingleResult();
        AclObjectIdentity aoi = new AclObjectIdentity();
        aoi.setEntriesInheriting(false);
        aoi.setObjectIdClass(AclClass.findAclClassesByClazzEquals(Menu.class.getCanonicalName()).getSingleResult());
        aoi.setObjectIdIdentity(menu.getId());
        aoi.setOwnerSid(sidOwner);
        aoi.persist();
        for (Perfil perfil : perfis) {
            AclSid sid = AclSid.findAclSidsByPerfil(perfil).getSingleResult();
            AclEntry ae = new AclEntry();
            ae.setAceOrder(AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoi));
            ae.setAclObjectIdentity(aoi);
            ae.setAuditFailure(true);
            ae.setAuditSuccess(true);
            ae.setGranting(true);
            ae.setMask(1);
            ae.setSid(sid);
            ae.persist();
            if (perfil.getAuthority().equals("ROLE_ADMIN")) {
                AclEntry ae2 = new AclEntry();
                ae2.setAceOrder(AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoi));
                ae2.setAclObjectIdentity(aoi);
                ae2.setAuditFailure(true);
                ae2.setAuditSuccess(true);
                ae2.setGranting(true);
                ae2.setMask(2);
                ae2.setSid(sid);
                ae2.persist();
            }
            if (menu.getPai() != null) {
                Menu pai = menu.getPai();
                AclObjectIdentity aoiPai = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(pai.getId()).getSingleResult();
                Integer proximoAclOrderPai = AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoiPai);
                if (AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoiPai, sid).getResultList().size() == 0) {
                    AclEntry aePai = new AclEntry();
                    aePai.setAceOrder(proximoAclOrderPai);
                    aePai.setAclObjectIdentity(aoiPai);
                    aePai.setAuditFailure(true);
                    aePai.setAuditSuccess(true);
                    aePai.setGranting(true);
                    aePai.setMask(1);
                    aePai.setSid(sid);
                    aePai.persist();
                }
            }
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity<String>(headers, HttpStatus.CREATED);
    }

    @RequestMapping(value = "/jsonArray", method = RequestMethod.POST, headers = "Accept=application/json")
    public ResponseEntity<java.lang.String> createFromJsonArray(@RequestBody String json) {
        for (Menu menu : Menu.fromJsonArrayToMenus(json)) {
            menuService.saveMenu(menu);
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity<String>(headers, HttpStatus.CREATED);
    }

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @RequestMapping(method = RequestMethod.PUT, headers = "Accept=application/json")
    @Transactional(rollbackFor = Throwable.class)
    public ResponseEntity<java.lang.String> updateFromJson(@RequestBody String json, Principal principal) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        JSONSerializer serializador = new JSONSerializer();
        JSONDeserializer deserializador = new JSONDeserializer();
        JSONDeserializer<ArrayList<Perfil>> deserializadorPerfil = new JSONDeserializer<ArrayList<Perfil>>();
        Object objetoTranscrito = deserializador.deserialize(json);
        HashMap<String, Object> hashMap = (HashMap<String, Object>) objetoTranscrito;
        Object perfisObjeto = hashMap.get("perfis");
        Object menuObjeto = hashMap.get("menu");
        ArrayList<Perfil> perfis = deserializadorPerfil.use(null, ArrayList.class).use("values", Perfil.class).deserialize(serializador.prettyPrint(false).deepSerialize(perfisObjeto));
        Menu menu = Menu.fromJsonToMenu(serializador.prettyPrint(false).deepSerialize(menuObjeto));
        if (menuService.updateMenu(menu) == null) {
            return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
        }
        AclObjectIdentity aoi = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(menu.getId()).getSingleResult();
        List<AclEntry> aclEntrys = AclEntry.findAclEntrysByAclObjectIdentity(aoi).getResultList();
        List<Perfil> perfisAntigos = new ArrayList<Perfil>();
        for (AclEntry aclEntry : aclEntrys) {
            Perfil perfil = Perfil.findPerfilsByAuthorityEquals(aclEntry.getSid().getSid()).getSingleResult();
            if (!perfisAntigos.contains(perfil)) {
                perfisAntigos.add(perfil);
            }
        }
        for (Perfil perfil : perfis) {
            AclSid sid = AclSid.findAclSidsByPerfil(perfil).getSingleResult();
            if (AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoi, sid).getResultList().size() == 0) {
                AclEntry ae = new AclEntry();
                ae.setAceOrder(AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoi));
                ae.setAclObjectIdentity(aoi);
                ae.setAuditFailure(true);
                ae.setAuditSuccess(true);
                ae.setGranting(true);
                ae.setMask(1);
                ae.setSid(sid);
                ae.persist();
                if (perfil.getAuthority().equals("ROLE_ADMIN")) {
                    AclEntry ae2 = new AclEntry();
                    ae2.setAceOrder(AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoi));
                    ae2.setAclObjectIdentity(aoi);
                    ae2.setAuditFailure(true);
                    ae2.setAuditSuccess(true);
                    ae2.setGranting(true);
                    ae2.setMask(2);
                    ae2.setSid(sid);
                    ae2.persist();
                }
                if (menu.getPai() != null) {
                    Menu pai = menu.getPai();
                    AclObjectIdentity aoiPai = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(pai.getId()).getSingleResult();
                    Integer proximoAclOrderPai = AclEntry.findMaxAclEntrysOrderByAclObjectIdentity(aoiPai);
                    if (AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoiPai, sid).getResultList().size() == 0) {
                        AclEntry aePai = new AclEntry();
                        aePai.setAceOrder(proximoAclOrderPai);
                        aePai.setAclObjectIdentity(aoiPai);
                        aePai.setAuditFailure(true);
                        aePai.setAuditSuccess(true);
                        aePai.setGranting(true);
                        aePai.setMask(1);
                        aePai.setSid(sid);
                        aePai.persist();
                    }
                }
            } else {
                continue;
            }
        }
        for (Perfil perfilAntigo : perfisAntigos) {
            if (!perfis.contains(perfilAntigo)) {
                AclSid sid = AclSid.findAclSidsBySidEquals(perfilAntigo.getAuthority()).getSingleResult();
                for (AclEntry aclEntry : AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoi, sid).getResultList()) {
                    aclEntry.remove();
                }
                if (menu.getPai() != null) {
                    Menu pai = menu.getPai();
                    boolean filhoUnico = true;
                    for (Menu filho : pai.getFilhos()) {
                        AclObjectIdentity aoiFilho = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(filho.getId()).getSingleResult();
                        if (!(AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoiFilho, sid).getResultList().size() == 0)) {
                            filhoUnico = false;
                        }
                    }
                    if (filhoUnico) {
                        AclObjectIdentity aoiPai = AclObjectIdentity.findAclObjectIdentitysByObjectIdIdentityEquals(pai.getId()).getSingleResult();
                        for (AclEntry aclEntry : AclEntry.findAclEntrysByAclObjectIdentityAndAclSid(aoiPai, sid).getResultList()) {
                            aclEntry.remove();
                        }
                    }
                }
            }
        }
        return new ResponseEntity<String>(headers, HttpStatus.OK);
    }

    @RequestMapping(value = "/jsonArray", method = RequestMethod.PUT, headers = "Accept=application/json")
    public ResponseEntity<java.lang.String> updateFromJsonArray(@RequestBody String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        for (Menu menu : Menu.fromJsonArrayToMenus(json)) {
            if (menuService.updateMenu(menu) == null) {
                return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
            }
        }
        return new ResponseEntity<String>(headers, HttpStatus.OK);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, headers = "Accept=application/json")
    public ResponseEntity<java.lang.String> deleteFromJson(@PathVariable("id") Long id) {
        Menu menu = menuService.findMenu(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        if (menu == null) {
            return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
        }
        menuService.deleteMenu(menu);
        return new ResponseEntity<String>(headers, HttpStatus.OK);
    }
}
